package org.absolutezero.client.util;

import java.util.ArrayList;
import java.util.List;

import org.absolutezero.client.di.Injector;
import org.absolutezero.client.events.NavigationChangeEvent;
import org.absolutezero.client.events.NavigationChangeEvent.NavigationItemSelected;
import org.absolutezero.client.style.Heading2Label;
import org.absolutezero.client.style.Heading3Label;
import org.absolutezero.shared.FridgeProductProxy;
import org.absolutezero.shared.FridgeProductRequest;
import org.absolutezero.shared.ProductProxy;
import org.absolutezero.shared.ProductRequest;
import org.absolutezero.shared.RecipeIngredientProxy;
import org.absolutezero.shared.RecipeProxy;
import org.absolutezero.shared.RecipeRequest;
import org.absolutezero.shared.RefrigeratorRequestFactory;
import org.absolutezero.shared.ShelfProxy;
import org.absolutezero.shared.ShelfRequest;
import org.absolutezero.shared.UserProxy;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestionEvent;
import com.google.gwt.user.client.ui.SuggestionHandler;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.ServerFailure;




public class FormFactory {
	
	private static EventBus ebus = Injector.INSTANCE.getEventBus();
	
	public static Panel getEditShelf(final ShelfProxy shelf,final RefrigeratorRequestFactory requestFactory){
		return getEditShelf(shelf, requestFactory, true);
	}
	
	@SuppressWarnings("deprecation")
	public static Panel getEditShelf(final ShelfProxy shelf,
			final RefrigeratorRequestFactory requestFactory, final boolean isMyShelf){
		
		String name;
		boolean isRead;
		boolean isWrite;
		
		if (shelf == null){
			name = "";
			isRead = false;
			isWrite = false;
		} else {
			name = shelf.getName();
			isRead = shelf.getPublicRead();
			isWrite = shelf.getPublicWrite();
		}
		
		final FlowPanel fPanel = new FlowPanel();
		
		final Grid grid = new Grid(4,2);
		final TextBox tbName = new TextBox();
		tbName.setText(name);
		final CheckBox cbRead = new CheckBox();
		cbRead.setValue(isRead);
		final CheckBox cbWrite = new CheckBox();
		cbWrite.setValue(isWrite);
		final Button btnSubmit = new Button("Submit Shelf");
		final List<FridgeProductProxy> displayedProducts = new ArrayList<FridgeProductProxy>();
		final List<ProductProxy> selected = new ArrayList<ProductProxy>();
		final ProductSuggestionOracle productsOracle = new ProductSuggestionOracle();
		ProductRequest request2 = requestFactory.productRequest();
		final Panel shProductsPanel = new VerticalPanel();
		final ShelfRequest request = requestFactory.shelfRequest();
		
		
		btnSubmit.addClickHandler(new ClickHandler(){

			
			@Override
			public void onClick(ClickEvent event) {
				
				
				ShelfProxy shelf2 = null;
				if (shelf == null){
					shelf2  = request.create(ShelfProxy.class);
				}
				else{
					shelf2 = request.edit(shelf);
				}
				
				if(isMyShelf){
					UserProxy user = (UserProxy)Session.get("logged-user");
					user = request.edit(user);
					
					shelf2.setOwner(user);
				}
				
				shelf2.setName(tbName.getText());
				shelf2.setPublicRead(cbRead.getValue());
				shelf2.setPublicWrite(cbWrite.getValue());
				shelf2.setFridgeProducts(displayedProducts);
			
				
				Request<Void> insertShelfRequest = request.persist().using(shelf2);
				
				insertShelfRequest.fire(new Receiver<Void>(){

					@Override
					public void onSuccess(Void response) {
						if(shelf == null){
							Dialog.show("Shelf insertion", "success");
						}
						else{
							Dialog.show("Shelf edit", "success");
						}
						
						ebus.fireEvent(new NavigationChangeEvent(NavigationChangeEvent.NavigationItemSelected.SHELF_MY));
					}
					
					@Override
					public void onFailure(ServerFailure f){
						if(shelf == null){
							Dialog.show("Shelf insertion failed",f.getMessage());
						}
						else{
							Dialog.show("Shelf edit failed",f.getMessage());
						}
						
					}
				
				});
				
			}
			
		});
		
		grid.setWidget(0, 0, tbName);
		grid.setWidget(1, 0, new Label("Public Read:"));
		grid.setWidget(1, 1, cbRead);
		grid.setWidget(2, 0, new Label("Public Write:"));
		grid.setWidget(2, 1, cbWrite);
		
		fPanel.add(new Heading2Label("Shelf Name"));
		fPanel.add(grid);
		fPanel.add(shProductsPanel);
		
		
		final FridgeProductRequest request3 = requestFactory.fridgeProductRequest();
		
		
		if (shelf != null){
			Request<List<FridgeProductProxy>> productReq = request3.getShelfProducts(shelf.getId()).with("product");
			productReq.fire(new Receiver<List<FridgeProductProxy>>(){
	
				@Override
				public void onSuccess(List<FridgeProductProxy> response) {
					
					displayedProducts.addAll(response);
					final CellTable<FridgeProductProxy> ct = CellFactory.getFridgeProductsTableView(displayedProducts);
					
					final MultiSelectionModel<FridgeProductProxy> sm = 
							(MultiSelectionModel<FridgeProductProxy>)ct.getSelectionModel();
					
					shProductsPanel.add(new Heading2Label("Shelf Products"));
					shProductsPanel.add(ct);
					Button btnRemove = new Button("Remove Selected Products");
					shProductsPanel.add(btnRemove);
					
					btnRemove.addClickHandler(new ClickHandler(){
	
						@Override public void onClick(ClickEvent event) {
							
							
							ShelfRequest request4 = requestFactory.shelfRequest();
							 final List<FridgeProductProxy> list 
								= new ArrayList<FridgeProductProxy>();
							
							for(FridgeProductProxy p : sm.getSelectedSet()){
								list.add(request4.edit(p));
							}
							
							 Request<Void> removeReq = request4.removeShelfProducts(list);
							 removeReq.fire(new Receiver<Void>(){

								@Override public void onSuccess(Void response) {
									displayedProducts.removeAll(sm.getSelectedSet());
									ct.setRowData(displayedProducts);
									ct.redraw();
								}
								@Override public void onFailure(ServerFailure f){
									Dialog.show("Fail",f.getMessage());
								}
								 
							 });
						}
					});
				}
				
				@Override
				public void onFailure(ServerFailure f){
					Dialog.show("Shelf products fetch failed",f.getMessage());			
				}
			});
		} else {
			final CellTable<FridgeProductProxy> ct = CellFactory.getFridgeProductsTableView(displayedProducts);
			
			final MultiSelectionModel<FridgeProductProxy> sm = 
					(MultiSelectionModel<FridgeProductProxy>)ct.getSelectionModel();
			
			shProductsPanel.add(new Heading2Label("Shelf Products"));
			shProductsPanel.add(ct);
			Button btnRemove = new Button("Remove Product");
			shProductsPanel.add(btnRemove);
			
			btnRemove.addClickHandler(new ClickHandler(){

				@Override public void onClick(ClickEvent event) {
					
					ShelfRequest request4 = requestFactory.shelfRequest();
					 final List<FridgeProductProxy> list 
						= new ArrayList<FridgeProductProxy>();
					
					for(FridgeProductProxy p : sm.getSelectedSet()){
						list.add(request4.edit(p));
					}
					
					 Request<Void> removeReq = request4.removeShelfProducts(list);
					 removeReq.fire(new Receiver<Void>(){

						@Override public void onSuccess(Void response) {
							displayedProducts.removeAll(sm.getSelectedSet());
							ct.setRowData(displayedProducts);
							ct.redraw();
						}
						@Override public void onFailure(ServerFailure f){
							Dialog.show("Fail",f.getMessage());
						}
						 
					 });
				}
			});
		}
		
		Request<List<ProductProxy>> fetchProductsRequest = request2.getAllProducts();
		
		fetchProductsRequest.fire(new Receiver<List<ProductProxy>>(){

			@Override
			public void onSuccess(List<ProductProxy> response) {
				for(ProductProxy p : response){
					productsOracle.add(new ProductSuggestion(p));
				}
			}
			
			@Override
			public void onFailure(ServerFailure f){
				Dialog.show("Fetch products failed", f.getMessage());				
			}
			
		});
		final SuggestBox suggestBox = new SuggestBox(productsOracle);
		suggestBox.setLimit(10);
		suggestBox.addEventHandler(new SuggestionHandler(){
				
			@Override
			public void onSuggestionSelected(SuggestionEvent e){
				ProductSuggestion ps = (ProductSuggestion)e.getSelectedSuggestion();
				selected.clear();
				selected.add(ps.getProduct());
			}
		});
		
		fPanel.add(new Heading2Label("Add Product"));
		
		Button btnAddProduct = new Button("Add Product");
		final TextBox tbQuantity = new TextBox();
		final TextBox tbExpirationDate = new TextBox();
		Grid editGrid = new Grid(4, 3);
		editGrid.setWidget(0, 0, new Label("Quantity (grams)") );
		editGrid.setWidget(0, 1, tbQuantity );
		editGrid.setWidget(1, 0, new Label("Expiration Date") );
		editGrid.setWidget(1, 1, tbExpirationDate);
		editGrid.setWidget(1, 2, new Label("Format: DD-MM-YYYY"));
		editGrid.setWidget(2, 0, new Label("Product") );
		editGrid.setWidget(2, 1, suggestBox);
		editGrid.setWidget(3, 1, btnAddProduct);
		
		fPanel.add(editGrid);
		
		final ClickHandler clickHandler = new ClickHandler(){

			
			@Override public void onClick(ClickEvent event) {
				
				FridgeProductProxy fpp = request.create(FridgeProductProxy.class);
				
				// FIXME now, if user types correct product name which exists in base
				// and when she/he does not select it in suggest box, it does not work
				
				String sbText = suggestBox.getText();
				
				if (selected.isEmpty())
				{
					Dialog.show("Wrong product name typed", "No product named " + sbText + " in base. Please use suggestion box hints.");
					return;
				}

				ProductProxy selectedProduct = selected.get(0);
				
				if (!sbText.equals(selectedProduct.getName()))
				{
					Dialog.show("Wrong product name typed", "To select product, please click on product name in suggestion box.");
					return;
				}

				fpp.setProduct(selectedProduct);				
				
				Long quantity = Long.valueOf(tbQuantity.getText());
				if (quantity <= 0)
				{
					Dialog.show("Wrong quantity", "Wrong quantity value. It must be greater than 0.");
					return;
				}

				fpp.setQuantity(quantity);
				
				try
				{
					DateTimeFormat format = DateTimeFormat.getFormat("dd-MM-yyyy");
					fpp.setExpiration(format.parse(tbExpirationDate.getText()));
				}
				catch (IllegalArgumentException e)
				{
					Dialog.show("Wrong date format", "Wrong date format, should be: dd-mm-yyyy");
					return;
				}
				
				displayedProducts.add(fpp);
				shProductsPanel.clear();
				final CellTable<FridgeProductProxy> ct = CellFactory.getFridgeProductsTableView(displayedProducts);
				final MultiSelectionModel<FridgeProductProxy> sm = 
						(MultiSelectionModel<FridgeProductProxy>)ct.getSelectionModel();
				
				shProductsPanel.add(new Heading2Label("Shelf Products"));
				shProductsPanel.add(ct);
				Button btnRemove = new Button("Remove Selected Products");
				shProductsPanel.add(btnRemove);
				
				btnRemove.addClickHandler(new ClickHandler(){

					@Override public void onClick(ClickEvent event) {
						
						ShelfRequest request4 = requestFactory.shelfRequest();
						 final List<FridgeProductProxy> list 
							= new ArrayList<FridgeProductProxy>();
						
						// FIXME some error with previously edit request?
						for(FridgeProductProxy p : sm.getSelectedSet()){
							list.add(request4.edit(p));
						}
						
						 Request<Void> removeReq = request4.removeShelfProducts(list);
						 removeReq.fire(new Receiver<Void>(){

							@Override public void onSuccess(Void response) {
								displayedProducts.removeAll(sm.getSelectedSet());
								ct.setRowData(displayedProducts);
								ct.redraw();
							}
							@Override public void onFailure(ServerFailure f){
								Dialog.show("Fail",f.getMessage());
							}
							 
						 });
					}
				});
			}
			
		};
		
		
		btnAddProduct.addClickHandler(clickHandler);
		
		fPanel.add(new Heading2Label(""));
		
		fPanel.add(btnSubmit);
		
		return fPanel;
	}
	
	
	
	
	@SuppressWarnings("deprecation")
	public static Panel getEditRecipe(final RecipeProxy r, 
								final RefrigeratorRequestFactory requestFactory){
		
		
		final RecipeProxy recipe;
		final List<ProductProxy> selected = new ArrayList<ProductProxy>();
		final List<RecipeIngredientProxy> ingredients = new ArrayList<RecipeIngredientProxy>();
		final RecipeRequest addRecipeRequest = requestFactory.recipeRequest();
		final VerticalPanel vpIngredients = new VerticalPanel();
		
		if (r == null){
			recipe = addRecipeRequest.create(RecipeProxy.class);
			recipe.setConsumersNum(0);
			recipe.setDuration(0);
			recipe.setText("");
			recipe.setName("");
		} else {
			recipe = r;
			RecipeRequest request2 = requestFactory.recipeRequest();
			Request<List<RecipeIngredientProxy>> fetchReq = request2.getRecipeIngredients(recipe.getId()).with("product");
			
			fetchReq.fire(new Receiver<List<RecipeIngredientProxy>>(){

				@Override
				public void onSuccess(List<RecipeIngredientProxy> response) {
				
					ingredients.addAll(response);
					for(RecipeIngredientProxy rip : response){
						vpIngredients.add(new Label(
								rip.getProduct().getName()+ ", " +rip.getQuantity().toString() + " g"));
					}
					
				}
				
				@Override
				public void onFailure(ServerFailure f){
					Dialog.show("Recipe fetch failed", f.getMessage());				
				}
				
			});
		}
		
		VerticalPanel vpanel = new VerticalPanel();
		
		final ProductSuggestionOracle productsOracle = new ProductSuggestionOracle();
		ProductRequest request = requestFactory.productRequest();
		Request<List<ProductProxy>> fetchProductsRequest = request.getAllProducts();
		
		fetchProductsRequest.fire(new Receiver<List<ProductProxy>>(){

			@Override
			public void onSuccess(List<ProductProxy> response) {
				for(ProductProxy p : response){
					productsOracle.add(new ProductSuggestion(p));
				}
			}
			
			@Override
			public void onFailure(ServerFailure f){
				Dialog.show("Fetch products failed", f.getMessage());				
			}
			
		});
		SuggestBox suggestBox = new SuggestBox(productsOracle);
		suggestBox.setLimit(10);
		suggestBox.addEventHandler(new SuggestionHandler(){
				
			@Override
			public void onSuggestionSelected(SuggestionEvent e){
				ProductSuggestion ps = (ProductSuggestion)e.getSelectedSuggestion();
				selected.clear();
				selected.add(ps.getProduct());
			}
		});
		
		
		
		final TextBox tbQuantity = new TextBox();
		Button btnAddIngredient = new Button("Add Ingredient");
		btnAddIngredient.addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				RecipeIngredientProxy rip = addRecipeRequest.create(RecipeIngredientProxy.class);
				rip.setProduct(selected.get(0));
				rip.setQuantity(Long.valueOf(tbQuantity.getText()));
				ingredients.add(rip);
				vpIngredients.add(new Label(
						rip.getProduct().getName()+ ", " +rip.getQuantity().toString() + " g"));
			}
			
		});
		
		final TextBox tbPreparationTime = new TextBox();
		tbPreparationTime.setText(recipe.getDuration().toString());
		final TextBox tbConsumersCount =new TextBox();
		tbConsumersCount.setText(recipe.getConsumersNum().toString());
		final TextArea taDescription = new TextArea();
		taDescription.setText(recipe.getText());
		taDescription.setWidth("500px");
		taDescription.setHeight("300px");
		final TextBox tbName = new TextBox();
		tbName.setText(recipe.getName());
		
		final Button btnAddRecipe = new Button("Add Recipe");
		btnAddRecipe.addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				
				RecipeRequest userreq = requestFactory.recipeRequest();
				UserProxy author = (UserProxy)Session.get("logged-user");
				author = userreq.edit(author);
				recipe.setAuthor(author);
				
				int consCount = 0, prepTime = 0; 
				
				try
				{
					consCount = Integer.valueOf(tbConsumersCount.getText());
					prepTime = Integer.valueOf(tbPreparationTime.getText());
				}
				catch (NumberFormatException e)
				{
					Dialog.show("Invalid value", "Consumers count and preparation time fields accept only integers.");
					return;
				}
				
				if (consCount <= 0 || prepTime <= 0)
				{
					Dialog.show("Invalid value", "Consumers count and preparation time fields accept only integers greater than 0.");
					return;
				}
				
				recipe.setConsumersNum(consCount);
				recipe.setDuration(prepTime);
				recipe.setName(tbName.getText());
				recipe.setAuthor(author);
				recipe.setIngredients(ingredients);
				recipe.setText(taDescription.getText());
				
				Request<Void> request = addRecipeRequest.persist().using(recipe);
				request.fire(new Receiver<Void>(){
					@Override
					public void onSuccess(Void response) {
						ebus.fireEvent(new NavigationChangeEvent(NavigationItemSelected.RECIPE_MY));
					}
					
					@Override
					public void onFailure(ServerFailure f){
						Dialog.show("Recipe add failed", f.getMessage());				
					}

				});
			}
			
		});
		FlowPanel fp1 = new FlowPanel();
		FlowPanel fp2 = new FlowPanel();
		FlowPanel fp3 = new FlowPanel();
		FlowPanel fp4 = new FlowPanel();
		
		fp4.add(new Heading2Label("Recipe Name"));
		fp4.add(tbName);
		vpanel.add(fp4);
		
		vpanel.add(new Heading2Label("Used Ingredients"));
		vpanel.add(vpIngredients);
		vpanel.add(new Heading2Label("Add Ingredient"));
		fp1.add(new Heading3Label("Ingredient"));
		fp1.add(suggestBox);
		fp1.add(new Heading3Label("Quantity (grams)"));
		fp1.add( tbQuantity);
		fp1.add( btnAddIngredient);
		fp1.setWidth("900px");
		vpanel.add(fp1);
		
		vpanel.add(new Heading2Label("Recipe"));
		vpanel.add(taDescription);
		fp2.add(new Heading3Label("Consumers Count"));
		fp2.add(tbConsumersCount);
		fp3.add( new Heading3Label("Preparation Time (minutes)"));
		fp3.add(tbPreparationTime);
		
		vpanel.add(fp2);
		vpanel.add(fp3);
		vpanel.add(btnAddRecipe);
		
		
		return vpanel;
	}
	
	public static Panel getViewShelf(final ShelfProxy shelf, 
								final RefrigeratorRequestFactory requestFactory){
		
		
		final Panel panel = new VerticalPanel();

		final FridgeProductRequest request = requestFactory.fridgeProductRequest();
		Request<List<FridgeProductProxy>> productReq = request.getShelfProducts(shelf.getId()).with("product");
		productReq.fire(new Receiver<List<FridgeProductProxy>>(){
	
				@Override
				public void onSuccess(List<FridgeProductProxy> response) {
					CellTable<FridgeProductProxy> ct = CellFactory.getFridgeProductsTableView(response);
					panel.add(ct);
				}
				
				@Override
				public void onFailure(ServerFailure f){
					Dialog.show("Shelf Products Shelf", f.getMessage());				
				}
		});
		
		return panel;
	}
	
	public static Panel getViewRecipe(final RecipeProxy recipe, 
								final RefrigeratorRequestFactory requestFactory){
		
		VerticalPanel mainPanel = new VerticalPanel();
		
		final HTML nutrients = new HTML("Loading data...");
		
		final VerticalPanel vpIngredients = new VerticalPanel();
		
		RecipeRequest request = requestFactory.recipeRequest();
		Request<List<RecipeIngredientProxy>> fetchReq = 
				request.getRecipeIngredients(recipe.getId()).with("product");
		
		fetchReq.fire(new Receiver<List<RecipeIngredientProxy>>(){

			@Override
			public void onSuccess(List<RecipeIngredientProxy> response) {
				
				int fat = 0;
				int protein = 0;
				int carbohydrate = 0;
				int calories = 0;
				
				for(RecipeIngredientProxy rip : response){
					ProductProxy product = rip.getProduct();
					Long quantity = rip.getQuantity();
					
					vpIngredients.add(new Label(
							product.getName()+ ", " + quantity.toString() + " g"));
					
					// FIXME how they are stored? (content/100g?)
					fat += product.getFat() * (float)quantity/100.0;
					protein += product.getProtein() * (float)quantity/100.00;
					carbohydrate += product.getCarbohydrate() * (float)quantity/100.0;
					calories += product.getCalories() * (float)quantity/100.0;
				}
				
				nutrients.setHTML("<ul>" +
						"<li><b>Fat: </b>" + fat + " g" +
						"<li><b>Protein: </b>" + protein +" g" +
						"<li><b>Carbohydrate: </b>" + carbohydrate  + " g" +
						"<li><b>Calories: </b>" + calories  + " kcal" +
						"</ul>"
						);
				
			}
			
			@Override
			public void onFailure(ServerFailure f){
				Dialog.show("Recipe fetch failed", f.getMessage());				
			}
			
		});
		
		mainPanel.add(new HTML("<b>Author:</b> " + recipe.getAuthor().getFullName()));
		
		mainPanel.add(new Heading2Label("Ingredients"));
		mainPanel.add(vpIngredients);
		mainPanel.add(new Heading2Label("Recipe Text"));
		
		String recStr = recipe.getText();
		HTML html = new HTML("<p>" + recStr.replaceAll("\n", "<br/>") + "</p>");
		mainPanel.add(html);
		
		mainPanel.add(new Heading2Label("Nutrients"));
		mainPanel.add(nutrients);
		
		
		mainPanel.add(new Heading2Label("Parameters"));
		mainPanel.add(new Label("Preparation time : "+ recipe.getDuration().toString() + " min"));
		mainPanel.add(new Label("Consumers : " + recipe.getConsumersNum().toString()));
		mainPanel.add(new Heading2Label(""));
		Button btnBack = new Button("Back to Recipe Browser");
		mainPanel.add(btnBack);
		
		btnBack.addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				ebus.fireEvent(new NavigationChangeEvent(NavigationItemSelected.RECIPE_BROWSE));
			}
			
		});
		
		return mainPanel;
	}
	
}
